A Brief NOTE Before Starting This Project!!!¶
NSE India has discontinued old website which is used by NSEPY, check this link for reference:¶
(https://github.com/swapniljariwala/nsepy/issues/252)
Now we have new updated library nsepython instead of old nsepy library¶
I tried using old library but It didn't work, it wasn't fetching any data¶
I'm using the new updated library; install it with below command:¶
pip install nsepython
Importing Libraries¶
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from nsepython import *
from datetime import date, timedelta
Filtering Warnings¶
import warnings
warnings.filterwarnings('ignore')
# I've already downloaded data .csv file from NSE website
top20_nifty_stocks = pd.read_csv('T20-GL-gainers-NIFTY-26-Jul-2024.csv')
top20_nifty_stocks
| Symbol | Open | High | Low | Prev. Close | LTP | %chng | Volume | Value | CA | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | SHRIRAMFIN | 2705.80 | 3044.00 | 2690.00 | 2679.00 | 2934.00 | 9.52 | 6439265 | 1.850831e+10 | 23-Jul-2024 |
| 1 | CIPLA | 1504.75 | 1600.00 | 1501.00 | 1500.05 | 1586.50 | 5.76 | 4686806 | 7.282687e+09 | 02-Aug-2024 |
| 2 | DIVISLAB | 4570.95 | 4810.00 | 4563.85 | 4547.00 | 4792.25 | 5.39 | 1262056 | 5.979785e+09 | 02-Aug-2024 |
| 3 | BHARTIARTL | 1450.25 | 1520.00 | 1449.15 | 1449.15 | 1511.75 | 4.32 | 9563621 | 1.433788e+10 | 11-Aug-2023 |
| 4 | APOLLOHOSP | 6433.00 | 6679.95 | 6393.55 | 6385.80 | 6650.00 | 4.14 | 432670 | 2.851676e+09 | 20-Feb-2024 |
| 5 | ADANIPORTS | 1495.90 | 1547.90 | 1490.00 | 1487.00 | 1540.75 | 3.61 | 4500404 | 6.888228e+09 | 14-Jun-2024 |
| 6 | WIPRO | 510.00 | 528.55 | 508.50 | 506.85 | 524.50 | 3.48 | 13074509 | 6.808681e+09 | 16-Jul-2024 |
| 7 | TATASTEEL | 158.75 | 162.95 | 158.21 | 157.39 | 162.62 | 3.32 | 45636115 | 7.387574e+09 | 21-Jun-2024 |
| 8 | ADANIENT | 2995.00 | 3109.00 | 2988.10 | 2973.50 | 3072.25 | 3.32 | 1939775 | 5.948320e+09 | 14-Jun-2024 |
| 9 | SUNPHARMA | 1670.45 | 1723.80 | 1665.00 | 1665.80 | 1721.00 | 3.31 | 4591025 | 7.803090e+09 | 12-Jul-2024 |
| 10 | SBILIFE | 1686.25 | 1777.70 | 1686.25 | 1695.40 | 1750.60 | 3.26 | 2982225 | 5.216359e+09 | 15-Mar-2024 |
| 11 | LTIM | 5599.85 | 5811.90 | 5585.05 | 5597.90 | 5780.00 | 3.25 | 497156 | 2.861366e+09 | 19-Jun-2024 |
| 12 | HCLTECH | 1602.80 | 1640.00 | 1594.00 | 1587.60 | 1638.80 | 3.22 | 2813950 | 4.572641e+09 | 23-Jul-2024 |
| 13 | HINDALCO | 660.00 | 668.50 | 655.00 | 646.55 | 667.05 | 3.17 | 4730509 | 3.137510e+09 | 14-Aug-2023 |
| 14 | INFY | 1825.35 | 1883.00 | 1825.35 | 1824.85 | 1880.00 | 3.02 | 11368877 | 2.128117e+10 | 31-May-2024 |
| 15 | HDFCLIFE | 683.15 | 709.80 | 682.05 | 683.15 | 703.25 | 2.94 | 8932963 | 6.259774e+09 | 21-Jun-2024 |
| 16 | COALINDIA | 498.25 | 511.70 | 498.25 | 494.00 | 508.45 | 2.93 | 20437719 | 1.038359e+10 | 20-Feb-2024 |
| 17 | JSWSTEEL | 880.00 | 908.80 | 875.30 | 874.50 | 899.00 | 2.80 | 2056321 | 1.848900e+09 | 09-Jul-2024 |
| 18 | M&M | 2821.60 | 2897.40 | 2790.00 | 2811.40 | 2881.80 | 2.50 | 2352247 | 6.729379e+09 | 05-Jul-2024 |
| 19 | ITC | 494.05 | 506.40 | 487.40 | 489.95 | 501.55 | 2.37 | 20735172 | 1.033047e+10 | 04-Jun-2024 |
List of all Top 20 Gainers¶
top20_stocks = top20_nifty_stocks['Symbol'].values
top20_stocks
array(['SHRIRAMFIN', 'CIPLA', 'DIVISLAB', 'BHARTIARTL', 'APOLLOHOSP',
'ADANIPORTS', 'WIPRO', 'TATASTEEL', 'ADANIENT', 'SUNPHARMA',
'SBILIFE', 'LTIM', 'HCLTECH', 'HINDALCO', 'INFY', 'HDFCLIFE',
'COALINDIA', 'JSWSTEEL', 'M&M', 'ITC'], dtype=object)
Defining Starting Date(i.e. 5 years back from today) & Ending Date(i.e. today)¶
# Define the start and end dates for the 5 year period
end_date = date.today()
start_date = end_date - timedelta(days=5*365)
end_date = end_date.strftime('%d-%m-%Y')
start_date = start_date.strftime('%d-%m-%Y')
print('start_date:', start_date)
print('end_date:', end_date)
start_date: 29-07-2019 end_date: 27-07-2024
PRIOR NOTE:¶
First I'm implementing a backtesting system for a single stock to see if everything is working perfectly.
Then I'm going to do it for all 20 top stocks. *The question is:*
Why I'm doing it:¶
As to create a merged dataframe which consists of all 20 stocks, the process requires very high computational power.
I already tried several times doing this, but I'm unable to process the code for the above mentioned problem.
So, What's the Solution?¶
The solution is streamlit app which is deployed online, you can check any stock analysis.
You can even download detailed interactive visualization!
1. Data Acquisition and Preparation¶
def get_stock_data(stock_name: str, start_date=start_date, end_date=end_date) -> pd.DataFrame:
"""
Retrieves historical stock data for the specified stock within the given date range(Default Date Range - 5 Years from today).
Args:
stock_name (str): The symbol of the stock for which to retrieve data.
start_date (str, optional): The starting date of the date range in the format 'DD-MM-YYYY'. Defaults to the global `start_date` variable.
end_date (str, optional): The ending date of the date range in the format 'DD-MM-YYYY'. Defaults to the global `end_date` variable.
Returns:
pd.DataFrame: A DataFrame containing the historical stock data with the following columns:
- Symbol: The stock symbol
- Date: The stock date records
- Year: The particular year
- High: The daily high price
- Low: The daily low price
- Open: The daily opening price
- Close: The daily closing price
- Volume: The daily trading volume
"""
# Generating Stock Data with `nsepython` library by providing symbol, start date & end date
# Values sorted ascending by 'CH_TIMESTAMP' column (i.e. by Date: start to end)
stock_data_raw = equity_history(stock_name, 'EQ', start_date, end_date)
# Selecting Important Column for our Analysis
important_cols = ['CH_SYMBOL', 'CH_TIMESTAMP', 'CH_TRADE_HIGH_PRICE', 'CH_TRADE_LOW_PRICE', 'CH_OPENING_PRICE', 'CH_CLOSING_PRICE', 'CH_TOT_TRADED_QTY']
# Sub-setting Stock Data with important columns only
stock_data = stock_data_raw[important_cols]
# Renaming columns
stock_data = stock_data.rename(columns={
'CH_SYMBOL': 'Symbol',
'CH_TIMESTAMP': 'Date',
'CH_TRADE_HIGH_PRICE': 'High',
'CH_TRADE_LOW_PRICE': 'Low',
'CH_OPENING_PRICE': 'Open',
'CH_CLOSING_PRICE': 'Close',
'CH_TOT_TRADED_QTY': 'Volume'
})
stock_data['Date'] = pd.to_datetime(stock_data['Date'])
stock_data['Year'] = stock_data['Date'].dt.year
stock_data = stock_data[['Symbol','Date','Year','Low','High','Open','Close','Volume']].sort_values(by='Date')
return stock_data
# Taking only the top stock for now; symbol named "SHRIRAMFIN"
SHRIRAMFIN = get_stock_data('SHRIRAMFIN')
SHRIRAMFIN
| Symbol | Date | Year | Low | High | Open | Close | Volume | |
|---|---|---|---|---|---|---|---|---|
| 1257 | SRTRANSFIN | 2019-07-29 | 2019 | 962.55 | 990.00 | 980.80 | 967.45 | 1417626 |
| 1258 | SRTRANSFIN | 2019-07-30 | 2019 | 964.10 | 982.40 | 967.50 | 969.00 | 1468107 |
| 1259 | SRTRANSFIN | 2019-07-31 | 2019 | 959.00 | 975.45 | 967.85 | 969.25 | 1528948 |
| 1260 | SRTRANSFIN | 2019-08-01 | 2019 | 963.30 | 984.70 | 969.00 | 967.35 | 1274941 |
| 1261 | SRTRANSFIN | 2019-08-02 | 2019 | 960.00 | 981.90 | 971.90 | 978.45 | 1315470 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 13 | SHRIRAMFIN | 2024-07-22 | 2024 | 2764.05 | 2847.95 | 2799.90 | 2827.40 | 543557 |
| 14 | SHRIRAMFIN | 2024-07-23 | 2024 | 2663.40 | 2827.00 | 2827.00 | 2739.20 | 2445493 |
| 15 | SHRIRAMFIN | 2024-07-24 | 2024 | 2665.30 | 2760.00 | 2739.00 | 2724.25 | 1026115 |
| 16 | SHRIRAMFIN | 2024-07-25 | 2024 | 2635.50 | 2710.00 | 2699.50 | 2679.00 | 2089082 |
| 17 | SHRIRAMFIN | 2024-07-26 | 2024 | 2690.00 | 3044.00 | 2705.80 | 2925.00 | 6439336 |
1284 rows × 8 columns
2. Implementing a Simple Moving Average(SMA) Crossover Strategy¶
def simple_moving_average_crossover(df):
"""
Implement a simple moving average crossover strategy.
Parameters:
df (pandas DataFrame): The historical data for a stock.
Returns:
pandas DataFrame: The backtested data with additional columns for strategy signals.
"""
# Calculate the 50-day and 200-day simple moving averages
df['50d'] = df['Close'].rolling(window=50).mean()
df['200d'] = df['Close'].rolling(window=200).mean()
# Generate buy/sell signals
df['Signal'] = 0.0
# Signal Value 1 depicts Buy Signal; 0 depicts Sell Signal
df['Signal'] = df['50d'] > df['200d'] # True if 50d > 200d, else False
df['Signal'] = df['Signal'].astype(int) # Convert boolean to int (1 for buy, 0 for sell)
# Buy Signal: When 50SMA line cross above 200SMA line, indicating Bull Market Sign
# Bear Signal: When 50SMA line cross below 200SMA line, indicating Bear Market Sign
# Create a column for entry/exit signals
df['Entry/Exit'] = df['Signal'].diff()
# Calculate strategy returns based on entry signals
df['Strategy Returns'] = df['Close'].pct_change() * df['Signal'].shift(1) # Use previous signal for returns
df['Cumulative Strategy Returns'] = (1 + df['Strategy Returns']).cumprod() - 1 # Cumulative returns
return df
simple_moving_average_crossover(SHRIRAMFIN)
| Symbol | Date | Year | Low | High | Open | Close | Volume | 50d | 200d | Signal | Entry/Exit | Strategy Returns | Cumulative Strategy Returns | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1257 | SRTRANSFIN | 2019-07-29 | 2019 | 962.55 | 990.00 | 980.80 | 967.45 | 1417626 | NaN | NaN | 0 | NaN | NaN | NaN |
| 1258 | SRTRANSFIN | 2019-07-30 | 2019 | 964.10 | 982.40 | 967.50 | 969.00 | 1468107 | NaN | NaN | 0 | 0.0 | 0.000000 | 0.000000 |
| 1259 | SRTRANSFIN | 2019-07-31 | 2019 | 959.00 | 975.45 | 967.85 | 969.25 | 1528948 | NaN | NaN | 0 | 0.0 | 0.000000 | 0.000000 |
| 1260 | SRTRANSFIN | 2019-08-01 | 2019 | 963.30 | 984.70 | 969.00 | 967.35 | 1274941 | NaN | NaN | 0 | 0.0 | -0.000000 | 0.000000 |
| 1261 | SRTRANSFIN | 2019-08-02 | 2019 | 960.00 | 981.90 | 971.90 | 978.45 | 1315470 | NaN | NaN | 0 | 0.0 | 0.000000 | 0.000000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 13 | SHRIRAMFIN | 2024-07-22 | 2024 | 2764.05 | 2847.95 | 2799.90 | 2827.40 | 543557 | 2636.055 | 2325.20975 | 1 | 0.0 | 0.005888 | 0.815245 |
| 14 | SHRIRAMFIN | 2024-07-23 | 2024 | 2663.40 | 2827.00 | 2827.00 | 2739.20 | 2445493 | 2644.232 | 2329.80125 | 1 | 0.0 | -0.031195 | 0.758619 |
| 15 | SHRIRAMFIN | 2024-07-24 | 2024 | 2665.30 | 2760.00 | 2739.00 | 2724.25 | 1026115 | 2652.317 | 2333.95825 | 1 | 0.0 | -0.005458 | 0.749020 |
| 16 | SHRIRAMFIN | 2024-07-25 | 2024 | 2635.50 | 2710.00 | 2699.50 | 2679.00 | 2089082 | 2659.123 | 2337.86025 | 1 | 0.0 | -0.016610 | 0.719969 |
| 17 | SHRIRAMFIN | 2024-07-26 | 2024 | 2690.00 | 3044.00 | 2705.80 | 2925.00 | 6439336 | 2670.191 | 2343.09475 | 1 | 0.0 | 0.091825 | 0.877906 |
1284 rows × 14 columns
3. Calculating Performance Metrics:¶
- Total returns
- Annualized returns
- Maximum drawdown
- Sharpe ratio
- Win/Loss ratio
- Number of trades executed
def calculate_performance_metrics(df):
"""
Calculate key performance metrics for the backtested data.
Parameters:
df (pandas DataFrame): The backtested data.
Returns:
pandas DataFrame: The backtested data with additional columns for performance metrics.
"""
# Calculate total returns
df['Total Returns'] = df['Strategy Returns'].cumprod() - 1
# Calculate annualized returns
# Assuming daily returns, we can calculate annualized returns as follows:
trading_days = 252 # Typical number of trading days in a year
df['Annualized Returns'] = (1 + df['Total Returns']) ** (trading_days / len(df)) - 1
# Calculate maximum drawdown
df['High Watermark'] = df['Strategy Returns'].cummax()
df['Drawdown'] = df['High Watermark'] - df['Strategy Returns']
df['Max Drawdown'] = df['Drawdown'].max()
# Calculate Sharpe ratio
risk_free_rate = 0.0 # Assuming a risk-free rate of 0 for simplicity
df['Sharpe Ratio'] = (df['Annualized Returns'] - risk_free_rate) / df['Strategy Returns'].std() * np.sqrt(trading_days)
# Calculate win/loss ratio
wins = df['Strategy Returns'][df['Strategy Returns'] > 0].count()
losses = df['Strategy Returns'][df['Strategy Returns'] <= 0].count()
df['Win/Loss Ratio'] = wins / losses if losses > 0 else wins
# Calculate number of trades executed
df['Number of Trades'] = df['Signal'].diff().ne(0).sum()
return df
# Apply the performance metrics calculation
calculate_performance_metrics(SHRIRAMFIN)
| Symbol | Date | Year | Low | High | Open | Close | Volume | 50d | 200d | ... | Strategy Returns | Cumulative Strategy Returns | Total Returns | Annualized Returns | High Watermark | Drawdown | Max Drawdown | Sharpe Ratio | Win/Loss Ratio | Number of Trades | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1257 | SRTRANSFIN | 2019-07-29 | 2019 | 962.55 | 990.00 | 980.80 | 967.45 | 1417626 | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | 0.27702 | NaN | 0.394565 | 8 |
| 1258 | SRTRANSFIN | 2019-07-30 | 2019 | 964.10 | 982.40 | 967.50 | 969.00 | 1468107 | NaN | NaN | ... | 0.000000 | 0.000000 | -1.0 | -1.0 | 0.000000 | 0.000000 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 1259 | SRTRANSFIN | 2019-07-31 | 2019 | 959.00 | 975.45 | 967.85 | 969.25 | 1528948 | NaN | NaN | ... | 0.000000 | 0.000000 | -1.0 | -1.0 | 0.000000 | 0.000000 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 1260 | SRTRANSFIN | 2019-08-01 | 2019 | 963.30 | 984.70 | 969.00 | 967.35 | 1274941 | NaN | NaN | ... | -0.000000 | 0.000000 | -1.0 | -1.0 | -0.000000 | 0.000000 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 1261 | SRTRANSFIN | 2019-08-02 | 2019 | 960.00 | 981.90 | 971.90 | 978.45 | 1315470 | NaN | NaN | ... | 0.000000 | 0.000000 | -1.0 | -1.0 | 0.000000 | 0.000000 | 0.27702 | -908.245604 | 0.394565 | 8 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 13 | SHRIRAMFIN | 2024-07-22 | 2024 | 2764.05 | 2847.95 | 2799.90 | 2827.40 | 543557 | 2636.055 | 2325.20975 | ... | 0.005888 | 0.815245 | -1.0 | -1.0 | 0.162227 | 0.156339 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 14 | SHRIRAMFIN | 2024-07-23 | 2024 | 2663.40 | 2827.00 | 2827.00 | 2739.20 | 2445493 | 2644.232 | 2329.80125 | ... | -0.031195 | 0.758619 | -1.0 | -1.0 | 0.162227 | 0.193421 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 15 | SHRIRAMFIN | 2024-07-24 | 2024 | 2665.30 | 2760.00 | 2739.00 | 2724.25 | 1026115 | 2652.317 | 2333.95825 | ... | -0.005458 | 0.749020 | -1.0 | -1.0 | 0.162227 | 0.167684 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 16 | SHRIRAMFIN | 2024-07-25 | 2024 | 2635.50 | 2710.00 | 2699.50 | 2679.00 | 2089082 | 2659.123 | 2337.86025 | ... | -0.016610 | 0.719969 | -1.0 | -1.0 | 0.162227 | 0.178837 | 0.27702 | -908.245604 | 0.394565 | 8 |
| 17 | SHRIRAMFIN | 2024-07-26 | 2024 | 2690.00 | 3044.00 | 2705.80 | 2925.00 | 6439336 | 2670.191 | 2343.09475 | ... | 0.091825 | 0.877906 | -1.0 | -1.0 | 0.162227 | 0.070401 | 0.27702 | -908.245604 | 0.394565 | 8 |
1284 rows × 22 columns
4. Analysis and Insights:¶
- Visualize the backtesting results using charts
- Analyze the results, identifying strengths and weaknesses of the strategy.
- Discuss how market conditions (e.g., bull vs. bear markets) affected the strategy's performance.
- Suggest potential improvements or modifications to the strategy based on your analysis.
Visualize Backtesting Results:¶
def plot_backtested_data(backtested_data: pd.DataFrame, stock_name: str):
df = backtested_data
# Create the tooltip text with labels for candlestick
df['tooltip'] = (
'Date: ' + df['Date'].dt.strftime('%Y-%m-%d') + '<br>' +
'Open: ' + df['Open'].astype(str) + '<br>' +
'Low: ' + df['Low'].astype(str) + '<br>' +
'Close: ' + df['Close'].astype(str) + '<br>' +
'High: ' + df['High'].astype(str) + '<br>' +
'Volume: ' + df['Volume'].astype(str)
)
# Create the candlestick trace
candlestick = go.Candlestick(
x=df['Date'], # Change x-axis to df['Date']
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Close'],
name='Candlestick',
hovertext=df['tooltip'], # Use the custom tooltip
hoverinfo='text' # Show custom tooltip
)
# Create the 50-day SMA trace
sma50 = go.Scatter(
x=df['Date'], # Change x-axis to df['Date']
y=df['50d'],
mode='lines',
name='50-day SMA',
line=dict(color='darkorchid')
)
# Create the 200-day SMA trace
sma200 = go.Scatter(
x=df['Date'], # Change x-axis to df['Date']
y=df['200d'],
mode='lines',
name='200-day SMA',
line=dict(color='violet')
)
# Create the buy signal trace with tooltip
buy_signals = df[df['Entry/Exit'] == 1]
buy_trace = go.Scatter(
x=buy_signals['Date'], # Change x-axis to df['Date']
y=buy_signals['Close'],
mode='markers',
marker=dict(
symbol='triangle-up',
size=25,
color='green'
),
name='Buy Signal',
hovertext=(
'Date: ' + buy_signals['Date'].dt.strftime('%Y-%m-%d') + '<br>' +
'Close: ' + buy_signals['Close'].astype(str) + '<br>' +
'Signal: Buy'
),
hoverinfo='text' # Show custom tooltip
)
# Create the sell signal trace with tooltip
sell_signals = df[df['Entry/Exit'] == -1]
sell_trace = go.Scatter(
x=sell_signals['Date'], # Change x-axis to df['Date']
y=sell_signals['Close'],
mode='markers',
marker=dict(
symbol='triangle-down',
size=25,
color='red'
),
name='Sell Signal',
hovertext=(
'Date: ' + sell_signals['Date'].dt.strftime('%Y-%m-%d') + '<br>' +
'Close: ' + sell_signals['Close'].astype(str) + '<br>' +
'Signal: Sell'
),
hoverinfo='text' # Show custom tooltip
)
# Create the layout with quarterly ticks
layout = go.Layout(
title=f"{stock_name} - Simple Moving Average Crossover Strategy",
xaxis_title="Date",
yaxis_title="Price",
xaxis_rangeslider_visible=False,
width=1700,
height=900,
xaxis=dict(
constrain='domain',
range=[df['Date'].min() - pd.Timedelta(days=15), df['Date'].max() + pd.Timedelta(days=15)], # Add padding
tickvals=pd.date_range(start=df['Date'].min(), end=df['Date'].max(), freq='Q'), # Quarterly ticks
ticktext=pd.date_range(start=df['Date'].min(), end=df['Date'].max(), freq='Q').strftime('%Y-%m') # Format ticks as Year-Month
)
)
# Create the figure and plot
fig = go.Figure(data=[candlestick, sma50, sma200, buy_trace, sell_trace], layout=layout)
fig.update_layout(template='plotly_white')
fig.show()
plot_backtested_data(SHRIRAMFIN, 'SHRIRAMFIN')
Breakdown of Performance Metrics:¶
# Breakdown of performance metrics on a yearly basis
def get_yearly_performance_metrics(backtested_data):
yearly_performance = backtested_data.groupby('Year').agg({
'Total Returns': ['mean', 'std'],
'Annualized Returns': ['mean', 'std'],
'Max Drawdown': ['mean', 'std'],
'Sharpe Ratio': ['mean', 'std'],
'Win/Loss Ratio': ['mean', 'std'],
'Number of Trades': ['mean', 'std']
})
# Flatten the multi-level column index
yearly_performance.columns = ['_'.join(col).strip() for col in yearly_performance.columns.values]
# Reset the index to convert the aggregated data to a DataFrame
yearly_performance = yearly_performance.reset_index()
# Rename the columns for clarity
yearly_performance.columns = ['Year'] + [col for col in yearly_performance.columns if col != 'Year']
# Returning Dataframe
return yearly_performance
# Outputting performance metrics on a yearly basis
get_yearly_performance_metrics(SHRIRAMFIN)
| Year | Total Returns_mean | Total Returns_std | Annualized Returns_mean | Annualized Returns_std | Max Drawdown_mean | Max Drawdown_std | Sharpe Ratio_mean | Sharpe Ratio_std | Win/Loss Ratio_mean | Win/Loss Ratio_std | Number of Trades_mean | Number of Trades_std | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019 | -1.0 | 0.0 | -1.0 | 0.0 | 0.27702 | 0.0 | -908.245604 | 0.0 | 0.394565 | 0.0 | 8.0 | 0.0 |
| 1 | 2020 | -1.0 | 0.0 | -1.0 | 0.0 | 0.27702 | 0.0 | -908.245604 | 0.0 | 0.394565 | 0.0 | 8.0 | 0.0 |
| 2 | 2021 | -1.0 | 0.0 | -1.0 | 0.0 | 0.27702 | 0.0 | -908.245604 | 0.0 | 0.394565 | 0.0 | 8.0 | 0.0 |
| 3 | 2022 | -1.0 | 0.0 | -1.0 | 0.0 | 0.27702 | 0.0 | -908.245604 | 0.0 | 0.394565 | 0.0 | 8.0 | 0.0 |
| 4 | 2023 | -1.0 | 0.0 | -1.0 | 0.0 | 0.27702 | 0.0 | -908.245604 | 0.0 | 0.394565 | 0.0 | 8.0 | 0.0 |
| 5 | 2024 | -1.0 | 0.0 | -1.0 | 0.0 | 0.27702 | 0.0 | -908.245604 | 0.0 | 0.394565 | 0.0 | 8.0 | 0.0 |
SHRIRAMFIN - Simple Moving Average Crossover Strategy Analysis¶
This chart depicts the price action of SHRIRAMFIN along with two simple moving averages (SMAs): a 50-day SMA and a 200-day SMA. This is a common technical analysis tool for identifying potential buy and sell signals.¶
Observations:¶
- Candlestick: The green candlestick represents an upward movement in price, while the red candlestick represents a downward movement in price.
- 50-day SMA: This SMA is a shorter-term indicator, showing the average price over the last 50 days.
- 200-day SMA: This SMA is a longer-term indicator, showing the average price over the last 200 days.
- Buy Signal: The green triangle indicates a buy signal. It is triggered when the 50-day SMA crosses above the 200-day SMA.
- Sell Signal: The red triangle indicates a sell signal. It is triggered when the 50-day SMA crosses below the 200-day SMA.
Analysis:¶
- Overall Trend: The chart shows a generally upward trend in the price of SHRIRAMFIN.
- Bullish Momentum: The 50-day SMA has crossed above the 200-day SMA on multiple occasions. This suggests that there is bullish momentum in the stock.
- Potential Buy Opportunities: The green triangles identify potential buy opportunities where the 50-day SMA crossed above the 200-day SMA, indicating a bullish crossover.
- Potential Sell Opportunities: The red triangles identify potential sell opportunities where the 50-day SMA crossed below the 200-day SMA, indicating a bearish crossover.
Conclusion:¶
The simple moving average crossover strategy is a useful tool for identifying potential buy and sell opportunities in SHRIRAMFIN. However, it is important to note that this is just one indicator and should be used in conjunction with other technical and fundamental analysis.
Easy BackTesting Analysis using backtesting python library¶
Getting Same Stock Data¶
data = get_stock_data('SHRIRAMFIN')
data
| Symbol | Date | Year | Low | High | Open | Close | Volume | |
|---|---|---|---|---|---|---|---|---|
| 1257 | SRTRANSFIN | 2019-07-29 | 2019 | 962.55 | 990.00 | 980.80 | 967.45 | 1417626 |
| 1258 | SRTRANSFIN | 2019-07-30 | 2019 | 964.10 | 982.40 | 967.50 | 969.00 | 1468107 |
| 1259 | SRTRANSFIN | 2019-07-31 | 2019 | 959.00 | 975.45 | 967.85 | 969.25 | 1528948 |
| 1260 | SRTRANSFIN | 2019-08-01 | 2019 | 963.30 | 984.70 | 969.00 | 967.35 | 1274941 |
| 1261 | SRTRANSFIN | 2019-08-02 | 2019 | 960.00 | 981.90 | 971.90 | 978.45 | 1315470 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 13 | SHRIRAMFIN | 2024-07-22 | 2024 | 2764.05 | 2847.95 | 2799.90 | 2827.40 | 543557 |
| 14 | SHRIRAMFIN | 2024-07-23 | 2024 | 2663.40 | 2827.00 | 2827.00 | 2739.20 | 2445493 |
| 15 | SHRIRAMFIN | 2024-07-24 | 2024 | 2665.30 | 2760.00 | 2739.00 | 2724.25 | 1026115 |
| 16 | SHRIRAMFIN | 2024-07-25 | 2024 | 2635.50 | 2710.00 | 2699.50 | 2679.00 | 2089082 |
| 17 | SHRIRAMFIN | 2024-07-26 | 2024 | 2690.00 | 3044.00 | 2705.80 | 2925.00 | 6439336 |
1284 rows × 8 columns
Making a class for Simple Moving Average Cross Strategy¶
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA
import pandas as pd
class SMACross(Strategy):
def init(self):
# Define two moving averages
self.sma50 = self.I(SMA, self.data.Close, 50) # Short-term MA
self.sma200 = self.I(SMA, self.data.Close, 200) # Long-term MA
def next(self):
# Buy when the short-term MA crosses above the long-term MA
if crossover(self.sma50, self.sma200):
self.buy()
# Sell when the short-term MA crosses below the long-term MA
elif crossover(self.sma200, self.sma50):
self.sell()
Printing Key Metrics(Stats)¶
backtest = Backtest(data, SMACross, commission=.002, exclusive_orders=True)
stats = backtest.run()
print(stats)
Start 0.0 End 1283.0 Duration 1283.0 Exposure Time [%] 61.137072 Equity Final [$] 3065.8235 Equity Peak [$] 10123.324 Return [%] -69.341765 Buy & Hold Return [%] -64.781054 Return (Ann.) [%] 0.0 Volatility (Ann.) [%] NaN Sharpe Ratio NaN Sortino Ratio NaN Calmar Ratio 0.0 Max. Drawdown [%] -79.137332 Avg. Drawdown [%] -79.137332 Max. Drawdown Duration 784.0 Avg. Drawdown Duration 784.0 # Trades 6.0 Win Rate [%] 0.0 Best Trade [%] -0.350177 Worst Trade [%] -43.687348 Avg. Trade [%] -20.744538 Max. Trade Duration 235.0 Avg. Trade Duration 130.666667 Profit Factor 0.0 Expectancy [%] -19.261707 SQN -2.094035 _strategy SMACross _equity_curve Equity... _trades Size EntryBa... dtype: object
Visualizing BackTesting Strategy¶
backtest.plot()